<?php
/**
 * 文件名简单介绍
 * 
 * 微信支付
 * @author  作者
 * @version  版本号
 * @date  2019-04-28
 */
namespace app\erp\designpattern\pay;
//require_once VENDOR_PATH."WechatPay/lib/WxPay.Api.php";
/**
 * 微信支付
 * 
 * 类的详细介绍（可选）
 * @author  作者
 * @version  版本号
 * @date  2020-02-02
 */
class Wechatpay implements PayInterface {
    //接口地址
    private $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
    //查询订单
    private $queryOrderUrl = 'https://api.mch.weixin.qq.com/pay/orderquery';

    //开放平台appid
    private $appId = 'wxf1ce5fccd088e469';
    //开放平台AppSecret
    private $appSecret = '5601f1bf973a3a374385fc48681b3e3b';
    //商户id号码
    private $mchId = '1600814121';//1507324531
    //商户秘钥（生成签名）
    private $apiKey = '4a9f2c433adcc2698ba7704faedeaf82';

	/**
     * 预支付订单
     * @param string $body 支付说明
     * @param string $out_trade_no 订单号
     * @param int $total_fee 支付金额
     * @param string $notify_url 回调地址
     * @return 
     */
    public function getPrePayOrder($body, $out_trade_no, $total_fee, $notify_url){
       
        $onoce_str = $this -> getRandChar(32);//获取指定长度的随机字符串
        $data["appid"]              = $this -> appId;
        $data["body"]               = $body;
        $data["mch_id"]             = $this -> mchId;
        $data["nonce_str"]          = $onoce_str;
        $data["notify_url"]         = $notify_url;
        $data["out_trade_no"]       = $out_trade_no;
        $data["spbill_create_ip"]   = $this -> get_client_ip();
        $data["total_fee"]          = $total_fee;
        $data["trade_type"]         = "APP";
        $sign = $this -> getSign($data, false);
        $data["sign"] = $sign;
        //dump($sign);
        $xml = $this -> arrayToXml($data);
        $response = $this -> postXmlCurl($xml, $this -> url);

        //将微信返回的结果xml转成数组
        return $this -> xmlToArray($response);
    }

    /**
     * 调起支付
     * @param string $body 支付说明
     * @param string $out_trade_no 订单号
     * @param int $total_fee 支付金额
     * @param string $notify_url 回调地址
     * @return 
     */
    public function transferPayment($prepayid){
        $onoce_str = $this -> getRandChar(32);//获取指定长度的随机字符串
        $data = [];
        $data['appid']      = $this -> appId;
        $data['partnerid']  = $this -> mchId;
        $data['prepayid']   = $prepayid;
        $data['package']    = 'Sign=WXPay';
        $data['noncestr']   = $onoce_str;
        $data['timestamp']  = time();
        $sign = $this -> getSign($data, false);
        $data['sign'] = $sign;

        $data['packageValue'] = $data['package'];
        unset($data['package']);
        return $data;
    }

    /**
     * 查询订单信息
     * @param string $out_trade_no 订单号
     * @return 
     */
    public function getOrderByOuttrano($out_trade_no){
        $onoce_str = $this -> getRandChar(32);//获取指定长度的随机字符串
        $data = [];
        $data['appid']      = $this -> appId;
        $data['mch_id']     = $this -> mchId;
        $data['transaction_id']   = $out_trade_no;
    
        $data['nonce_str']   = $onoce_str;

        $sign = $this -> getSign($data, false);
        $data['sign'] = $sign;

        //$data['transaction_id']   = $out_trade_no;

        $xml = $this -> arrayToXml($data);
        $response = $this -> postXmlCurl($xml, $this -> queryOrderUrl);
        //dump($out_trade_no);dump($sign);dump($xml);
        //将微信返回的结果xml转成数组
        return $this -> xmlToArray($response);
    }

    /**
     * 作用：将xml转为array
     */
    public function xmlToArray($xml)
    {       
       //将XML转为array        
       $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);       
        return $array_data;
    }

    /**
     * 获取指定长度的随机字符串
     * @param int $length 长度
     * @return 
     */
    private function getRandChar($length){
        $str = null;
        $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
        $max = strlen($strPol)-1;

        for($i=0;$i<$length;$i++){
            $str.=$strPol[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
        }

        return $str;
    }

    /**
     * 获取当前服务器IP
     * @param 
     * @return 
     */
    private function get_client_ip(){
        if ($_SERVER['REMOTE_ADDR']) {
            $cip = $_SERVER['REMOTE_ADDR'];
        } elseif (getenv("REMOTE_ADDR")) {
            $cip = getenv("REMOTE_ADDR");
        } elseif (getenv("HTTP_CLIENT_IP")) {
            $cip = getenv("HTTP_CLIENT_IP");
        } else {
            $cip = "unknown";
        }
        return $cip;
    }

    /**
     * 生成签名
     * @param 
     * @return 
     */
    public function getSign($Obj){
        foreach ($Obj as $k => $v) {
            $Parameters[strtolower($k)] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this -> formatBizQueryParaMap($Parameters, false);
        //echo "【string】 =".$String."</br>";
        //签名步骤二：在string后加入KEY
        $String = $String."&key=".$this -> apiKey;//dump($String);
//        echo "<textarea style='width: 50%; height: 150px;'>$String</textarea> <br />";
        //签名步骤三：MD5加密
        $result_ = strtoupper(md5($String));
        return $result_;
    }

    //将数组转成uri字符串
    private function formatBizQueryParaMap($paraMap, $urlencode) {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v)
        {
            if($urlencode)
            {
                $v = urlencode($v);
            }
            $buff .= strtolower($k) . "=" . $v . "&";
        }
        $reqPar;
        if (strlen($buff) > 0)
        {
            $reqPar = substr($buff, 0, strlen($buff)-1);
        }
        return $reqPar;
    }


    //数组转xml
    private function arrayToXml($arr) {
        $xml = "<xml>";
        foreach ($arr as $key=>$val)
        {
            if (is_numeric($val))
            {
                $xml.="<".$key.">".$val."</".$key.">";

            }
            else
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
        }
        $xml.="</xml>";
        return $xml;
    }

    //post https请求，CURLOPT_POSTFIELDS xml格式
    private function postXmlCurl($xml,$url,$second=30) {
        //初始化curl
        $ch = curl_init();
        //超时时间
        curl_setopt($ch,CURLOPT_TIMEOUT,$second);
        //这里设置代理，如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if($data)
        {
            curl_close($ch);
            return $data;
        }
        else
        {
            $error = curl_errno($ch);
            echo "curl出错，错误码:$error"."<br>";
            echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
            curl_close($ch);
            return false;
        }
    }
}